using System;

namespace IQIGame.Onigao.Game
{
    public struct Vector2Logic : IEquatable<Vector2Logic>
    {
        public float x;
        public float y;

        private static readonly Vector2Logic s_Zero = new Vector2Logic(0f, 0f);
        private static readonly Vector2Logic s_One = new Vector2Logic(1f, 1f);
        private static readonly Vector2Logic s_Up = new Vector2Logic(0f, 1f);
        private static readonly Vector2Logic s_Down = new Vector2Logic(0f, -1f);
        private static readonly Vector2Logic s_Left = new Vector2Logic(-1f, 0f);
        private static readonly Vector2Logic s_Right = new Vector2Logic(1f, 0f);

        public static Vector2Logic zero => s_Zero;
        public static Vector2Logic one => s_One;
        public static Vector2Logic up => s_Up;
        public static Vector2Logic down => s_Down;
        public static Vector2Logic left => s_Left;
        public static Vector2Logic right => s_Right;

        public Vector2Logic(float x, float y)
        {
            this.x = x;
            this.y = y;
        }

        public void Set(float x, float y)
        {
            this.x = x;
            this.y = y;
        }

        public float sqrMagnitude
        {
            get
            {
                return x * x + y * y;
            }
        }

        public float magnitude
        {
            get
            {
                return (float)Math.Sqrt(sqrMagnitude);
            }
        }

        public Vector2Logic normalized
        {
            get
            {
                return Normalized(this);
            }
        }

        private static Vector2Logic Normalized(Vector2Logic v)
        {
            float magnitude = v.magnitude;
            if (magnitude > float.Epsilon)
            {
                return v / magnitude;
            }
            return zero;
        }

        public static Vector2Logic Min(Vector2Logic lhs, Vector2Logic rhs)
        {
            return new Vector2Logic(Math.Min(lhs.x, rhs.x), Math.Min(lhs.y, rhs.y));
        }

        /// <summary>
        ///   <para>Returns a vector that is made from the largest components of two vectors.</para>
        /// </summary>
        /// <param name="lhs"></param>
        /// <param name="rhs"></param>
        public static Vector2Logic Max(Vector2Logic lhs, Vector2Logic rhs)
        {
            return new Vector2Logic(Math.Max(lhs.x, rhs.x), Math.Max(lhs.y, rhs.y));
        }

        /// <summary>
        ///   <para>Multiplies two vectors component-wise.</para>
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        public static Vector2Logic Scale(Vector2Logic a, Vector2Logic b)
        {
            return new Vector2Logic(a.x * b.x, a.y * b.y);
        }

        /// <summary>
        ///   <para>Multiplies every component of this vector by the same component of scale.</para>
        /// </summary>
        /// <param name="scale"></param>
        public void Scale(Vector2Logic scale)
        {
            x *= scale.x;
            y *= scale.y;
        }

        /// <summary>
        ///   <para>Clamps the Vector3Int to the bounds given by min and max.</para>
        /// </summary>
        /// <param name="min"></param>
        /// <param name="max"></param>
        public void Clamp(Vector2Logic min, Vector2Logic max)
        {
            x = Math.Max(min.x, x);
            x = Math.Min(max.x, x);
            y = Math.Max(min.y, y);
            y = Math.Min(max.y, y);
        }

        public static float Dot(Vector2Logic lhs, Vector2Logic rhs)
        {
            return lhs.x * rhs.x + lhs.y * rhs.y;
        }

        public static implicit operator Vector3Logic(Vector2Logic v)
        {
            return new Vector3Logic(v.x, v.y, 0);
        }

        public static implicit operator Vector2Logic(Vector3Logic v)
        {
            return new Vector2Logic(v.x, v.y);
        }

        public static Vector2Logic operator +(Vector2Logic a, Vector2Logic b)
        {
            return new Vector2Logic(a.x + b.x, a.y + b.y);
        }

        public static Vector2Logic operator -(Vector2Logic a, Vector2Logic b)
        {
            return new Vector2Logic(a.x - b.x, a.y - b.y);
        }

        public static Vector2Logic operator *(Vector2Logic a, Vector2Logic b)
        {
            return new Vector2Logic(a.x * b.x, a.y * b.y);
        }

        public static Vector2Logic operator -(Vector2Logic a)
        {
            return new Vector2Logic(-a.x, -a.y);
        }

        public static Vector2Logic operator *(Vector2Logic a, float b)
        {
            return new Vector2Logic(a.x * b, a.y * b);
        }

        public static Vector2Logic operator *(float a, Vector2Logic b)
        {
            return new Vector2Logic(a * b.x, a * b.y);
        }

        public static Vector2Logic operator /(Vector2Logic a, float b)
        {
            return new Vector2Logic(a.x / b, a.y / b);
        }

        public static bool operator ==(Vector2Logic lhs, Vector2Logic rhs)
        {
            return lhs.x == rhs.x && lhs.y == rhs.y;
        }

        public static bool operator !=(Vector2Logic lhs, Vector2Logic rhs)
        {
            return !(lhs == rhs);
        }

        /// <summary>
        ///   <para>Returns true if the objects are equal.</para>
        /// </summary>
        /// <param name="other"></param>
        public override bool Equals(object other)
        {
            if (!(other is Vector2Logic))
            {
                return false;
            }
            return Equals((Vector2Logic)other);
        }

        public bool Equals(Vector2Logic other)
        {
            return this == other;
        }

        /// <summary>
        ///   <para>Gets the hash code for the Vector3Int.</para>
        /// </summary>
        /// <returns>
        ///   <para>The hash code of the Vector3Int.</para>
        /// </returns>
        public override int GetHashCode()
        {
            int hashCode = y.GetHashCode();
            return x.GetHashCode() ^ (hashCode << 4) ^ (hashCode >> 28);
        }

        /// <summary>
        ///   <para>Returns a formatted string for this vector.</para>
        /// </summary>
        /// <param name="format">A numeric format string.</param>
        /// <param name="formatProvider">An object that specifies culture-specific formatting.</param>
        public override string ToString()
        {
            return $"[{x}, {y}]";
        }
    }
}
